react-linear-feedback 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Oliver Odgaard
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,131 @@
1
+ # react-linear-feedback
2
+
3
+ A drop-in feedback widget for any React app. A trusted user opens it with `?feedback`, **drags a box** over the page, picks **Bug / Improvement**, writes a note β€” and it captures an **annotated screenshot** and opens a **Linear issue**.
4
+
5
+ - πŸͺΆ **Framework-agnostic** β€” Next.js, Vite, Remix, CRA… (no `next` dependency)
6
+ - 🎨 **Self-contained styles** β€” injected at runtime, themeable with one prop; **no CSS import, no Tailwind, no design system** required
7
+ - πŸ–ΌοΈ **Annotated screenshots** via [`modern-screenshot`](https://github.com/qq15725/modern-screenshot) (handles Tailwind v4 / `oklch()`)
8
+ - 🏷️ **Labels resolved by name** at request time (recoloring/recreating a label in Linear won't break it), applied best-effort
9
+ - πŸ”Œ Tiny server core + **Next.js & Node/Express adapters**
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ npm i react-linear-feedback
15
+ # the server entry needs the Linear SDK:
16
+ npm i @linear/sdk
17
+ ```
18
+
19
+ `react` / `react-dom` are peer dependencies. `@linear/sdk` is an optional peer β€” only needed where you run the server handler.
20
+
21
+ ## Quick start β€” Next.js (App Router)
22
+
23
+ **1. Server route** β€” `app/api/feedback/route.ts`:
24
+
25
+ ```ts
26
+ import { createNextRoute, cookieGate } from "react-linear-feedback/server";
27
+
28
+ export const runtime = "nodejs"; // needs Buffer + the Linear SDK
29
+
30
+ export const POST = createNextRoute({
31
+ apiKey: process.env.LINEAR_API_KEY!,
32
+ teamId: process.env.LINEAR_TEAM_ID!,
33
+ authorize: cookieGate("wh_feedback"), // only allow users who enabled the tool
34
+ });
35
+ ```
36
+
37
+ **2. Mount the widget** once (e.g. in `app/layout.tsx`):
38
+
39
+ ```tsx
40
+ import { FeedbackGate } from "react-linear-feedback/react";
41
+
42
+ export default function RootLayout({ children }) {
43
+ return (
44
+ <html><body>
45
+ {children}
46
+ <FeedbackGate brandColor="#7f56d9" />
47
+ </body></html>
48
+ );
49
+ }
50
+ ```
51
+
52
+ Visit any page with `?feedback` to turn it on (a cookie remembers it). `?feedback=0` turns it off.
53
+
54
+ ## Quick start β€” Vite / SPA (separate backend)
55
+
56
+ Mount the widget and point it at your backend:
57
+
58
+ ```tsx
59
+ import { FeedbackGate } from "react-linear-feedback/react";
60
+
61
+ <FeedbackGate endpoint="https://api.example.com/feedback" brandColor="#7f56d9" />
62
+ ```
63
+
64
+ Run the handler on any Node server (Express shown):
65
+
66
+ ```ts
67
+ import express from "express";
68
+ import { createNodeHandler } from "react-linear-feedback/server";
69
+
70
+ const app = express();
71
+ app.post("/feedback", createNodeHandler({
72
+ apiKey: process.env.LINEAR_API_KEY!,
73
+ teamId: process.env.LINEAR_TEAM_ID!,
74
+ }));
75
+ app.listen(8787);
76
+ ```
77
+
78
+ (Enable CORS for your site origin if the API is on a different host.)
79
+
80
+ ## Configuration
81
+
82
+ **`<FeedbackGate>` / `<FeedbackWidget>` props**
83
+
84
+ | Prop | Default | Description |
85
+ | --- | --- | --- |
86
+ | `endpoint` | `/api/feedback` | Where the widget POSTs |
87
+ | `brandColor` | indigo | FAB / active / focus color (sets `--lfb-brand`) |
88
+ | `position` | `bottom-right` | `bottom-right` \| `bottom-left` \| `top-right` \| `top-left` |
89
+ | `types` | Bug, Improvement | `{ id, label, color, icon? }[]` shown in the composer |
90
+ | `nameRequired` | `true` | Ask for a reporter name before the first submission |
91
+ | `fabLabel` | `Give feedback` | Floating button text |
92
+ | `urlParam` | `feedback` | Toggle param (gate only) |
93
+ | `cookieName` | `wh_feedback` | Enabled-state cookie (gate only) |
94
+
95
+ **Server config** (`createNextRoute` / `createNodeHandler` / `createFeedbackIssue`)
96
+
97
+ | Field | Description |
98
+ | --- | --- |
99
+ | `apiKey` | Linear personal API key (**server-side only**) |
100
+ | `teamId` | Target team UUID |
101
+ | `labels` | Optional `{ [typeId]: labelName }` map. Default: the type id is the label name (so `bug` β†’ label `bug`) |
102
+ | `allowedOrigin` | Optional single-origin allowlist |
103
+ | `authorize(req)` | Optional gate; return `false` to reject (`cookieGate` provided) |
104
+
105
+ ## Theming
106
+
107
+ Set `brandColor`, or override any CSS variable on `.lfb-doc-layer, .lfb-fixed-layer`:
108
+ `--lfb-brand`, `--lfb-fg`, `--lfb-surface`, `--lfb-border`, `--lfb-radius`, `--lfb-rect`, `--lfb-z`, `--lfb-font`.
109
+
110
+ ## Custom types
111
+
112
+ ```tsx
113
+ <FeedbackGate
114
+ types={[
115
+ { id: "bug", label: "Bug", color: "#ef4444", icon: "bug" },
116
+ { id: "idea", label: "Idea", color: "#22c55e", icon: "improvement" },
117
+ ]}
118
+ />
119
+ ```
120
+
121
+ Each `type.id` is matched to a Linear label of the same name (or remap via the server `labels` config).
122
+
123
+ ## Security
124
+
125
+ The endpoint **creates Linear issues**, so it's effectively write access to your tracker. It's gated only by what you wire up β€” use `authorize` (e.g. `cookieGate`, a session check) and/or `allowedOrigin`, and add rate limiting if the page is public. Your `LINEAR_API_KEY` stays server-side; the package never ships it to the browser.
126
+
127
+ > Linear asset URLs (the screenshots) are **private** β€” they render inside the issue but a fresh signed URL is needed to fetch them elsewhere.
128
+
129
+ ## License
130
+
131
+ MIT